package com.lex.service.impl;

import cn.hutool.core.lang.Validator;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.lex.common.Constants;
import com.lex.common.api.CommonResult;
import com.lex.common.factory.AsyncFactory;
import com.lex.common.factory.AsyncManager;
import com.lex.common.redis.RedisService;
import com.lex.data.admin.mapper.LexCmsMenuMapper;
import com.lex.data.admin.mapper.LexCmsRoleMenuMapper;
import com.lex.data.admin.mapper.LexCmsUserRoleMapper;
import com.lex.data.admin.mapper.LexCmsUsersMapper;
import com.lex.data.admin.model.*;
import com.lex.entity.user.LoginUserInfoVo;
import com.lex.entity.user.RoleInfoVo;
import com.lex.entity.user.UserResourceVo;
import com.lex.mapper.UserRoleMenu;
import com.lex.security.util.JwtTokenUtil;
import com.lex.service.BaseService;
import com.lex.service.SystemCacheService;
import com.lex.util.AdminUserDetails;
import com.lex.util.MyContext;
import com.lex.common.utils.SpringUtil;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class BaseServiceImpl implements BaseService {

    private final Integer LOGIN_RETRAY_TIMES = 5; // 登录重试次数
    @Resource
    private LexCmsMenuMapper lexCmsMenuMapper;

    @Resource
    private LexCmsRoleMenuMapper lexCmsRoleMenuMapper;

    @Resource
    private LexCmsUserRoleMapper lexCmsUserRoleMapper;

    @Resource
    private LexCmsUsersMapper lexCmsUsersMapper;

    @Resource
    private UserRoleMenu userRoleMenu;

    @Autowired
    private BCryptPasswordEncoder passwordEncoder;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Autowired
    private RedisService redisService;

    private List<UserResourceVo> getUserResourceList(List<Long> resourceIds){
        resourceIds = resourceIds != null ? resourceIds : List.of();
        List<UserResourceVo> resources = null;
        List<LexCmsMenu> allResources = getCacheService().getAllResourceList();
        if(allResources == null || allResources.isEmpty()){
            //把resourceIds中包含的资源过滤出来
            allResources = lexCmsMenuMapper.selectList(
                    new QueryWrapper<LexCmsMenu>().eq("state",1)
                            .orderByAsc("order_num")
            );
            getCacheService().setAllResourceList(allResources);
        }
        List<Long> finalResourceIds = resourceIds;
        resources = allResources.stream().filter(e -> {
                    return finalResourceIds.contains(e.getId())
                            && !"".equals(e.getPermission());
                })
                .map(e -> {
                    UserResourceVo resource = new UserResourceVo();
                    resource.setUrl(e.getPermission());
                    resource.setMethod(e.getMethod());
                    return resource;
                }).collect(Collectors.toList());
        return resources;
    }
    @Override
    public UserDetails getUserInfo(String username) {
        LexCmsUsers user = getUserInfoByAccount(username);
        if(user != null){
            //查询用户拥有的权限
            List<UserResourceVo> resources = null;
            Long user_id = user.getUserId();
            //查询用户角色
            LexCmsUserRole userRole = lexCmsUserRoleMapper.selectOne(
                    new QueryWrapper<LexCmsUserRole>().eq("user_id", user_id)
            );
            if(userRole != null){
                //查询角色拥有的权限
                List<LexCmsRoleMenu> roleMenus = lexCmsRoleMenuMapper.selectList(
                        new QueryWrapper<LexCmsRoleMenu>().eq("role_id", userRole.getRoleId())
                );
                List<Long> resourceIds =  roleMenus != null && !roleMenus.isEmpty()
                        ? roleMenus.stream().map(LexCmsRoleMenu::getMenuId).toList()
                        : null;
                resources = getUserResourceList(resourceIds);
            }

            return new AdminUserDetails(user,resources);
        }
        return new AdminUserDetails();
    }

    @Override
    public UserDetails loadUserByUsername(String username) {
        return getUserInfo(username);
    }
    /**
     * 获取用户信息
     * @param account
     * @return LexCmsUsers
     *
     */
    @Override
    public LexCmsUsers getUserInfoByAccount(String account) {
        LexCmsUsers user = getCacheService().getLexCmsUser(account);
        if (user != null) {
            MyContext.setCurrentUserName(user.getAccount());
            return user;
        }
        QueryWrapper<LexCmsUsers> queryWrapper = new QueryWrapper<>();
        if (Validator.isMobile(account)){
            queryWrapper.eq("phoneNum",account);
        }else if(Validator.isEmail(account)){
            queryWrapper.eq("email",account);
        }else{
            queryWrapper.eq("account",account);
        }
        LexCmsUsers userInfo = lexCmsUsersMapper.selectOne(queryWrapper);
        if (userInfo != null) {
            user = userInfo;
            getCacheService().setLexCmsFormUser(userInfo);
            MyContext.setCurrentUserName(user.getAccount());
            return user;
        }
        return null;
    }

    //获取缓存信息bean
    @Override
    public SystemCacheService getCacheService() {
        return SpringUtil.getBean(SystemCacheService.class);
    }
    //登录后获取用户信息
    @Override
    public CommonResult getLoginUserInfo() {
        String username = MyContext.getCurrentUserName();
        if (username == null) {
            return CommonResult.unauthorized(null);
        }
        LoginUserInfoVo userinfo = new LoginUserInfoVo();
        LexCmsUsers info = getUserInfoByAccount(username);
        userinfo.setUsername(username);
        userinfo.setRealName(info.getAccount());
        userinfo.setUserId(info.getUserId());
        userinfo.setAvatar(info.getAvatar());
        userinfo.setPhoneNum(info.getPhoneNum());
        List<LexCmsRole> roles = userRoleMenu.getUserRoleList(info.getUserId());
        List<RoleInfoVo> roleinfos = roles.stream().map(e -> {
            RoleInfoVo roleInfoVo = new RoleInfoVo();
            roleInfoVo.setRoleName(e.getRoleName());
            roleInfoVo.setValue(e.getRemark());
            return roleInfoVo;
        }).toList();
        userinfo.setRoles(roleinfos);
        return CommonResult.success(userinfo);
    }
    //登录
    @Override
    public CommonResult login(String username, String password) {
        //如果有验证码之类的，先验证验证码，否则验证码错误会加入到登录重试次数中
        //查询登录重试次数，如果大于5次，返回错误信息
        String retryTimes = Optional.ofNullable(redisService.get("login_retry_times_" + username)).orElse("0").toString();
        if (retryTimes != null && Integer.parseInt(retryTimes) >= LOGIN_RETRAY_TIMES) {
            AsyncManager.factory().execute(AsyncFactory.insertLoginLog(username, Constants.LOGIN_FAIL, "登录失败次数过多，请30分钟后再试！"));
            return CommonResult.failed("登录失败次数过多，请30分钟后再试！");
        }
        try {
            UserDetails userDetails = getUserInfo(username);
            if(!passwordEncoder.matches(password,userDetails.getPassword())){
                retryTimes =  retryTimes == null
                        ? "1"
                        : String.valueOf(Integer.parseInt(retryTimes) + 1);
                //设置登录重试次数
                redisService.set("login_retry_times_" + username,retryTimes, 1800);
                AsyncManager.factory().execute(AsyncFactory.insertLoginLog(username, Constants.LOGIN_FAIL, "用户名或密码错误！"));
                return CommonResult.failed("用户名或密码错误！失败"+LOGIN_RETRAY_TIMES+"次后将锁定30分钟！"+"当前失败次数："+retryTimes+"次");
            }
            if(!userDetails.isEnabled()){
                AsyncManager.factory().execute(AsyncFactory.insertLoginLog(username, Constants.LOGIN_FAIL, "账号已被禁用！"));
                return CommonResult.failed("账号已被禁用！");
            }
            //判断角色是否被禁用 待写
            UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
            SecurityContextHolder.getContext().setAuthentication(authentication);
            Map<String, String> tokenMap = generateToken(userDetails);
            //登录成功后删除登录重试次数
            redisService.del("login_retry_times_" + username);
            AsyncManager.factory().execute(AsyncFactory.insertLoginLog(username, Constants.LOGIN_SUCCESS, "登录成功！"));
            return CommonResult.success(tokenMap);
        } catch (AuthenticationException e) {
            log.warn("登录异常:{}", e.getMessage());
        }
        return CommonResult.failed("用户名或密码错误！");
    }

    /**
     * 退出登录，将jwt token加入黑名单
     * @return
     */
    @Override
    public CommonResult logout(String token) {
        List<String> black_list;
        black_list = getCacheService().getJwtBlackList() == null
                ? new ArrayList<>()
                : getCacheService().getJwtBlackList();
        black_list.add(token);
        getCacheService().setJwtBlackList(black_list);
        return CommonResult.success(null);
    }

    private Map<String, String> generateToken(UserDetails userDetails){
        String token = "";
        String refresh_token = "";
        token = jwtTokenUtil.generateToken(userDetails);
        refresh_token = jwtTokenUtil.generateRefreshToken(userDetails);
        String expiration = jwtTokenUtil.getRefreshExpiredDateFromToken(refresh_token).toString();
        Map<String, String> tokenMap = new HashMap<String,String>();
        tokenMap.put("accessToken", token);
        tokenMap.put("refreshToken", refresh_token);
        tokenMap.put("expiration", expiration);
        return tokenMap;
    }

}
